#ifndef ROOTBOX_H_JM_20120710
#define ROOTBOX_H_JM_20120710
/** @file RootBox.h _OK_ Bazova trieda pre boxy v jm4bbx.
 * 
 * Bazova trieda pre vsetky boxy vytvarane v pojekte zahrnutom v diplomovej 
 * praci. Vytvara spolocny a hlavne stabilny zaklad pre dedene boxy, kedze 
 * roznymi verziami  boboxu dochadzalo k zmenam. Naviac je v bazovej triede
 * doplnena schopnost riadiacich signalov.
 **/
#include "../LogConf.h"
#include "GenericBoxModel.h"
#include "groupAdapter/GroupAdapterInterface.h"
#include <map>
#include <utility>

namespace jm4bbx {

//#define BOX_LOG_S(m) LOG("BoxId: " << getBoxInstanceId() << " Message: " << m )
//#define BOX_LOG_L(m) BOX_LOG_S(m << "\n Function: "<< __FUNCTION__  << std::endl)

	/**
	 * Bazova trieda pre boxy v diplomke, ktora rozsiruje chovanie boxov o 
	 * riadiace signaly. Signaly su klasicke spojenia boxov (via), ktore NEnesu 
	 * data. Takteto boxy mozu mat 0..* vstupnych signalov a 0..* vystupnych. 
	 * Pocet vstupnych a vystupnych signalov nemusi byt zhodny. Boxy naviac mozu
	 * definovat klasicky spojenia ako je to pre boxy bezne.
	 * 
	 * Signaly su urcene pomocou parametrov boxu v modele. Jedna sa  o parametre
	 * konkretne pomenovane ako {@link RootBox::SIGNAL_IN_PARAMETER_NAME} a 
	 * {@link RootBox::SIGNAL_OUT_PARAMETER_NAME}. Cislena hodnota predana 
	 * v parametre znaci pocet signalov daneho typu.
	 * 
	 * Zdedena trieda ma pre obsluhu riadiacich signalov k dispozicii metody, 
	 * ktorymi je moze zahajit cakanie na vsetky vstupne sihnaly 
	 * {@link #wait4SignalsIn} alebo odoslat vsetky definovane vystupne signaly 
	 * {@link sendSignalsOut}. 
	 * 
	 * @tparam BoxInterface definicia IO rozhrania boxu. 
	 **/
	class RootBox : public basicBox {
	public:
		static std::string timeReportPrefix;

		JM_USE_EXPLICIT_LOGGER(BoxRunTracer)
		JM_USE_EXPLICIT_LOGGER(TimeTracer)
		/// Spristupnenie implementaciu v/v group boboxu pre potreby adapteru.
		/// V starsej verzii je totiz protected.
		/// @see GroupAdapterInterface
		/// @seee getGroup(groupType)
		using basicBox::group;

		typedef std::shared_ptr<GroupAdapterInterface> GroupPtr;

		/// Typ mapy drziacej sadu v/v skupin podla indexu fyzickeho v/v, ktory 
		/// nastavuju. Uz raz vytvorene groupy treba drzat v pamati pretoze
		/// niektore funkcie vyzadovane od spolocneho rozhrania vyzaduju 
		/// drzanie stavu. Vytvaranim novych adapterov by sa stav stratil.
		typedef std::map<int, GroupPtr> GroupMap;

		/// Meno-popisok paramtru pre vstupne signly v modele vypoctu.
		static const char* SIGNAL_IN_PARAMETER_NAME;
		/// Meno-popisok paramtru pre vystupne signly v modele vypoctu.
		static const char* SIGNAL_OUT_PARAMETER_NAME;

		static const char* TIME_REPORT_FACTOR_PARAM_NAME;
		/**
		 * @param paramsPack sada parametrov potrebna pre konstrukciu boxu
		 * 
		 * @param forceSignalInCount nasilne prepisanie hodnoty parametra 
		 * SIGNAL_IN_PARAMETER_NAME z definicie txt. modelu. Zaporna 
		 * hodnota znaci, ze sa ma pouzivat definicia z modelu. Kladna hodnota
		 * znamena prepisanie hodnoty parametra.
		 * 
		 * @param forceSignalOutCount nasilne prepisanie hodnoty parametra 
		 * SIGNAL_OUT_PARAMETER_NAME z definicie txt. modelu. Zaporna 
		 * hodnota znaci, ze sa ma pouzivat definicia z modelu. Kladna hodnota
		 * znamena prepisanie hodnoty parametra.
		 **/
		RootBox(const BoxParamPack& paramsPack, 
			int forceSignalInCount = -1, int forceSignalOutCount = -1);

		virtual ~RootBox() { }
		/**
		 * Vrati logiku v/v skupinu zodpovedajucu fyzickemu v/v boxu.
		 * Umoznuje zistenie stavu v/v a tiez zmenu stavu v/v.
		 * 
		 * @see Group
		 * 
		 * @param idx fyzicky v/v boxu (resp. index fyzickeho v/v boxu)
		 **/
		GroupPtr  getGroupPtr(groupType idx);

		/// Jedinecne id boxu v ramci jeho typu. Urcene pre ladenie.
		inline int getBoxInstanceId() const { return boxInstanceId_; }


		/// Spristupnenie alokacie pamate pre v/v groupy pre potreby adapteru.
		/// V starsej verzii je totiz protected.
		/// @see GroupAdapterInterface
		/// @see GroupAdapter
		void allocate(group_index_type idx, unsigned capacity) {
			basicBox::allocate(idx, capacity);
		}

				/**
		 * Nacitanie boolovskeho parametru boxu z definicie v modele zadaneho 
		 * pomocou cisla (0 == false, inak true).
		 * 
		 * @param p balicek parametrov boxu obsahujuci aj mapu parametrov 
		 * definicie boxu z modelu vypoctu
		 * 
		 * @param paramName meno nacitaneho parametra v modele vypoctu
		 *
		 * @param defaultValue hodnota, ktora sa pouzije ak sa parameter
		 * v mdodele nenachadza (volitelna, defaultne je to false)
		 **/
		static bool getBoxBoolParameterValue(const BoxParamPack& p, 
			const std::string& paramName, 
			const bool defaultValue = false);

		/**
		 * Nacitanie parametru boxu, z definicie v modele. 
		 * 
		 * @param p balicek parametrov boxu obsahujuci aj mapu parametrov 
		 * definicie boxu z modelu vypoctu
		 * 
		 * @param paramName meno nacitaho parametra
		 * 
		 * @param defaultValue defaultna hodnota parametra, ktora sa pouzije
		 * pokial sa parameter nenachadza v stupnej mape
		 * 
		 * @tparam ValueType typ nacitaneho parametra
		 **/
		template<typename ValueType>
		static ValueType getBoxParameterValue(const BoxParamPack& p, 
			const std::string& paramName, 
			const ValueType& defaultValue) {


				ValueType value = defaultValue;
				if (p.getPlainParameters()->has_parameter(paramName)) {
					std::string v;
					p.getPlainParameters()->get_parameter(paramName, v);
					std::stringstream stream(v);
					stream >> value;
				}
				return value;
		}

	protected:		
		/// Vykonat casovy report pred spustenim syncMachEtwas
		bool timeReport_;
		/**
		 * Inicializacia boxu volana medzi konstrukciou a spustenim vypoctu 
		 * boxu. Box si moze nastavit vlastne skupiny vstupov/vystupov a zahajit 
		 * cakanie na vstup.
		 **/
		virtual void initImpl() = 0;

		
		virtual void syncMachEtwas() = 0;

		virtual void sync_mach_etwas() override;

		/**
		 * Bola ukoncena komunikacia pre vstupne signaly resp. jedna sa 
		 * o ukoncovaciu spravu pre vstupne signaly?
		 **/
		bool areSignalsInPoisoned();

		/**
		 * Posle vsetky vystupne signaly. 
		 * 
		 * @param poisoned vystupne signaly budu poslane ako koncove a uzavru
		 * spojenie
		 **/
		void sendSignalsOut(bool poisoned = false);

		/**
		 * Posle konkretny vystupny signal, ktory je v modele definovany
		 * na pozadovanom indexe. 
		 * 
		 * @param index poradie definicie napojenia signalu v modele
		 * 
		 * @param poisoned vystupny signal bude poslany ako koncovy a uzavre
		 * spojenie
		 **/
		void sendSignalOut(int index, bool poisoned  = false  );

		/**
		 * Zahaji cakanie na vsetky vstupne signaly.
		 **/
		void wait4SignalsIn();  

		/// Nastavenie poctu vstpnych signalov fungueje do a pocas zavolania 
		/// metody {@link #initImpl()}, po inicializcii uz zmena nastavenie 
		/// ziaden vplyv.
		void setSignalsIn(int value) { signalInCount_ = value; }

		/// Nastavenie vystpneho signalu fungueje do a pocas zavolania 
		/// metody {@link #initImpl()}, po inicializcii uz zmena nastavenie 
		/// ziaden vplyv.
		void setSignalsOut(int value) { signalOutCount_ = value; }

		/**
		 * Povodna inicializacia skupin pre vstupne aj vystupne signaly.
		 * Pre inicializaciu zdedeneho boxu je urcena metoda {@link initImpl()}.
		 * 
		 * @warning Pokial dojde k pretazeniu tejto metody je potrebne 
		 * implementaciu z bazovej triedy RootBox vyvolat rucne. Inak nebude
		 * fungovat funkcionalita riadiacich signalov a tiez sa nezavola 
		 * inicializacna metota {@link initImpl()}.
		 **/
		virtual void init_impl() override;

		std::string getBoxClassType();
		void reportMainMeanTime();
	private:

		/// Index fyzickeho vstupneho stlpca pre signal.
		/// Len formalna poziadavka signaly datove stlpce nepotrebuju.
		static const columnType SIGNAL_IN_COLUMN;
		/// Index fyzickeho vystupneho stlpca pre signal.
		/// Len formalna poziadavka signaly datove stlpce nepotrebuju.
		static const columnType SIGNAL_OUT_COLUMN;
		/// Index prvehej vstupnje skupiny pre vstupne signaly. 
		/// Kazdy signal ma vlsatnu skupinu a tedaj fyzicky vstup do boxu.
		/// Dalsie skupiny sa indexuju inkrementalne od prveho.
		/// @warninig Skupiny sa nerozdeluju na vstupne a vystupne. 
		/// Je teda potrebne rozlisit indexy pre vstupne a vystupne skupiny 
		/// vstupnych a vystupnych signalov.
		/// Index je schvalne nenulovy tak aby prilis neobmedzoval potomkov pri 
		/// vybere vlastnych indexov (bezne sa indexuje od 0).
		/// Avsak indexy rovne a vacsie ako zvoleny prvy su vyhradene pre 
		/// signaly.
		static const int SIGNAL_IN_GROUP_START_INDEX = 1000;
		/// Index prveho fyzickeho vstupupu pre vstupne signaly. 
		/// Kazdy signal ma vlsatnu skupinu a tedaj fyzicky vstup do boxu.
		/// Dalsie vstupy sa indexuju inkrementalne od prveho.
		/// Index je schvalne nenulovy tak aby prilis neobmedzoval potomkov pri 
		/// vybere vlastnych indexov (bezne sa indexuje od 0).
		/// Avsak indexy rovne a vacsie ako zvoleny prvy su vyhradene pre 
		/// signaly.
		static const int SIGNAL_IN_ARC_START_INDEX = 1000;
		/// Index prvehej vystupnje skupiny pre vystupne signaly. 
		/// Kazdy signal ma vlsatnu skupinu a tedaj fyzicky vystup z boxu.
		/// Dalsie skupiny sa indexuju inkrementalne od prveho.
		/// @warninig Skupiny sa nerozdeluju na vstupne a vystupne. 
		/// Je teda potrebne rozlisit indexy pre vstupne a vystupne skupiny 
		/// vstupnych a vystupnych signalov.
		/// Index je schvalne nenulovy tak aby prilis neobmedzoval potomkov pri 
		/// vybere vlastnych indexov (bezne sa indexuje od 0).
		/// Avsak indexy rovne a vacsie ako zvoleny prvy su vyhradene pre 
		/// signaly.
		static const int SIGNAL_OUT_GROUP_START_INDEX = 2000;
		/// Index prveho fyzickeho vystupupu pre vystupne signaly. 
		/// Kazdy signal ma vlsatnu skupinu a tedaj fyzicky vystup z boxu.
		/// Dalsie vystupy sa indexuju inkrementalne od prveho.
		/// Index je schvalne nenulovy tak aby prilis neobmedzoval potomkov pri 
		/// vybere vlastnych indexov (bezne sa indexuje od 0).
		/// Avsak indexy rovne a vacsie ako zvoleny prvy su vyhradene pre 
		/// signaly.
		static const int SIGNAL_OUT_ARC_START_INDEX = 1000;

		/// Citac id boxov pre generovanie unikatnych id.
		/// @see boxInstanceId_
		static int nextBoxInstanceId_;
		/// Unikatny identifikator boxu - vhodny asi len pre ladenie.
		int boxInstanceId_;
		/// Pocet vstupnych signalov do boxu.
		int signalInCount_;
		/// Pocet vystupnych signalov z boxu.
		int signalOutCount_;

		/// Prepocita logicky index na realny index pouzivany v boboxe.
		/// Logicke indexovanie je postupne ocislovanie (0, 1, 2 ...), realne 
		/// su vsak pouzite vysie indexy aby neboli obmedzovane odovodene boxy 
		/// pri vybere svojich indexov.
		static groupType getSignalInGroup(const int logicGroupIndex) {
			return groupType(SIGNAL_IN_GROUP_START_INDEX + logicGroupIndex);
		}  

		/// Prepocita logicky index na realny index pouzivany v boboxe.
		/// Logicke indexovanie je postupne ocislovanie (0, 1, 2 ...), realne 
		/// su vsak pouzite vysie indexy aby neboli obmedzovane odovodene boxy 
		/// pri vybere svojich indexov.
		static inType getSignalInArc(const int logicArcIndex) {
			return inType(SIGNAL_IN_ARC_START_INDEX + logicArcIndex);
		}  

		/// Prepocita logicky index na realny index pouzivany v boboxe.
		/// Logicke indexovanie je postupne ocislovanie (0, 1, 2 ...), realne 
		/// su vsak pouzite vysie indexy aby neboli obmedzovane odovodene boxy 
		/// pri vybere svojich indexov.
		static groupType getSignalOutGroup(const int logicGroupIndex) {
			return groupType(SIGNAL_OUT_GROUP_START_INDEX + logicGroupIndex);
		}  

		/// Prepocita logicky index na realny index pouzivany v boboxe.
		/// Logicke indexovanie je postupne ocislovanie (0, 1, 2 ...), realne 
		/// su vsak pouzite vysie indexy aby neboli obmedzovane odovodene boxy 
		/// pri vybere svojich indexov.
		static outType getSignalOutArc(const int logicArcIndex) {
			return outType(SIGNAL_OUT_ARC_START_INDEX + logicArcIndex);
		}  

		/// Inicializacia skupin pre vstupne signaly.		
		void initSignalInGroup();

		/// Inicializacia skupin pre vystupne signaly.
		void initSignalOutGroup();

	};

} // namespace jm4bbx

#endif
